package org.jrfe.res.impl;

import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map.Entry;

import org.jrfe.res.Sound;
import org.jrfe.res.SoundManager;
import org.jrfe.res.SoundType;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.lwjgl.util.WaveData;

public class OpenALSoundManagerImpl implements SoundManager {

	private HashMap<String, Sound> table = new HashMap<String, Sound>();

	private boolean inited = false;
	private int channels;

	private int[] sources;

	private int cEffectSource = 0;

	public OpenALSoundManagerImpl() {

	}

	
	@Override
	public boolean isInitialized() {
		return inited;
	}

	@Override
	public boolean initilizeManager(Object... pars) {

		if (pars.length == 0) {
			channels = 6;
			System.out.println("Using SoundManager Defaults");

		} else {
			System.out.println("Using SoundManager User Setting");

			try {
				channels = Integer.parseInt(pars[0].toString());
			} catch (Exception e) {
				channels = 6;
				System.out
						.println("Using SoundManager Default channel count: 6");
			}
		}

		try {
			AL.create();

			IntBuffer sourcesBuf = BufferUtils.createIntBuffer(channels);
			// allocate sources
			AL10.alGenSources(sourcesBuf);

			sources = new int[sourcesBuf.capacity()];

			sourcesBuf.get(sources);

			// could we allocate all channels?
			if (AL10.alGetError() != AL10.AL_NO_ERROR) {
				throw new LWJGLException("Unable to allocate " + channels
						+ " sources");
			}

			inited = true;
		} catch (LWJGLException le) {
			le.printStackTrace();
			System.out.println("Sound disabled");
		}

		return inited;
	}

	@Override
	public void destoryManager() {
		if (inited) {
			IntBuffer ib = BufferUtils.createIntBuffer(sources.length);
			ib.put(sources);
			ib.flip();
			AL10.alSourceStop(ib);

			AL10.alDeleteSources(ib);

			int[] buf = new int[table.size()];

			int i = 0;
			for (Entry<String, Sound> ent : table.entrySet()) {
				buf[i] = ent.getValue().getBuffer();
				i++;
			}
			ib = BufferUtils.createIntBuffer(buf.length);
			ib.put(buf);
			ib.flip();

			AL10.alDeleteBuffers(ib);

			// destory OpenAL
			AL.destroy();

		}
	}

	private int genBuffer() {
		IntBuffer buf = BufferUtils.createIntBuffer(1);
		AL10.alGenBuffers(buf);
		return buf.get(0);
	}

	@Override
	public Sound loadSound(String path) {
		if (inited) {

			Sound snd = table.get(path);
			if (snd != null) {
				return snd;
			}
			// load wave data from buffer
			WaveData wavefile = WaveData.create(path);

			if (wavefile != null) {

				int buf = genBuffer();

				//TODO Update This with names
				snd = new Sound(buf,"", path);

				// copy to buffers
				AL10.alBufferData(buf, wavefile.format, wavefile.data,
						wavefile.samplerate);

				table.put(path, snd);

				// unload file again
				wavefile.dispose();

				return snd;

			}
		}
		return null;
	}

	@Override
	public void play(Sound snd, SoundType type) {
		if (inited) {
			switch (type) {
			case EFFECT:

				AL10.alSourcei(sources[cEffectSource], AL10.AL_BUFFER, snd
						.getBuffer());
				AL10.alSourcePlay(sources[cEffectSource]);

				cEffectSource++;
				if (cEffectSource == sources.length - 2) {
					cEffectSource = 0;
				}
				break;
			case MUSIC:

				AL10.alSourcei(sources[sources.length - 1], AL10.AL_BUFFER, snd
						.getBuffer());
				AL10.alSourcePlay(sources[sources.length - 1]);
				break;
			}
		}
	}

	@Override
	public void stop() {
		if (inited) {
			if (isPlaying()) {
				AL10.alSourceStop(sources[sources.length - 1]);
			}
		}
	}

	@Override
	public boolean isPlaying() {
		if (inited) {
			return AL10.alGetSourcei(sources[sources.length - 1],
					AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING;
		}
		return false;
	}
	
	@Override
	public void destroySound(Sound snd) {
		if (inited) {
			IntBuffer ib = BufferUtils.createIntBuffer(1);
			ib.put(0, snd.getBuffer());
			AL10.alDeleteBuffers(ib);
			table.remove(snd.getResourcePath());
		}	
	}

}
